/* ----------------------------------------------------------------------------
 * Copyright (c) Huawei Technologies Co., Ltd. 2013-2019. All rights reserved.
 * Description: Ld Type
 * Author: zhangfanfan
 * Create: 2013-01-01
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 * --------------------------------------------------------------------------- */

/** @defgroup dynload dynamic loading
 * @ingroup kernel
 */

#ifndef _KERNEL_LOS_LD_TYPE_INC
#define _KERNEL_LOS_LD_TYPE_INC

#include "los_ld_elf.inc"
#include "los_ld_macro.inc"

#include "los_list.h"
#include "los_typedef_pri.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */

typedef AARCHPTR AARCHUINT;

#ifdef LOSCFG_AARCH64
#define UINTMAX                             UINT64_MAX
#else
#define UINTMAX                             UINT32_MAX
#endif

/* Maximum number of modules that are allowed to be loaded by Huawei LiteOS. */
#define N_MAX_USER_MODULES                  10

#define N_MAX_MODULES                       (N_MAX_USER_MODULES + 1)

/* Maximum number of default paths */
#define N_DEFAULT_PATH                      10

extern LOS_DL_LIST g_freeHandleList;
extern VOID *g_dynloadHeap;

/* Symbol types */
#define ELF_STT_NOTYPE                      0x0 /* Unknown type */
#define ELF_STT_OBJECT                      0x1 /* Data object */
#define ELF_STT_FUNC                        0x2 /* Function */
#define ELF_STT_SECTION                     0x3 /* Section */
#define ELF_STT_FILE                        0x4 /* File name */

/* Symbol binding information */
#define ELF_STB_LOCAL                       0x0 /* Local */
#define ELF_STB_GLOBAL                      0x1 /* Global */
#define ELF_STB_WEAK                        0x2 /* Weak reference */

/* Information about the section in which a symbol is located. The following are constants. */
#define ELF_SHN_ABS                         0xFFF1 /* Absolute symbol value, for example, a file name. */
#define ELF_SHN_COMMON                      0xFFF2 /* COMMON block */
#define ELF_SHN_UNDEF                       0x0000 /* Undefined symbol */

/* Symbol types */
#define OS_SHN_UNDF                         0x0 /* Undefined */
#define OS_SHN_LOCAL                        0x0 /* Local */
#define OS_SHN_GLOBAL                       0x1 /* Global (external) (ORed) */
#define OS_SHN_ABS                          0x2 /* Absolute */
#define OS_SHN_TEXT                         0x4 /* Text */
#define OS_SHN_DATA                         0x6 /* Data */
#define OS_SHN_BSS                          0x8 /* BSS */
#define OS_SHN_COMM                         0x10 /* Common symbol */
#define OS_SHN_THUMB                        0x80 /* Thumb function */

/* New load flags */
#define OS_NO_SYMBOLS                       0x1 /* No symbols added to the target
                                       symbol table
                                       */
#define OS_LOCAL_SYMBOLS                    0x2 /* Only internal symbols added to
                                       the target symbol table
                                       */
#define OS_GLOBAL_SYMBOLS                   0x4 /* Only external symbols added to
                                       the target symbol table
                                       */
#define OS_ALL_SYMBOLS                      (OS_LOCAL_SYMBOLS | OS_GLOBAL_SYMBOLS) /* All symbols added to the target
                                                                      symbol table
                                                                      */

#define ELF_BIND_LOCAL                      0
#define ELF_BIND_GLOBAL                     1
#define ELF_BIND_WEAK                       2
#define ELF_BIND_LOPROC                     13
#define ELF_BIND_HIPROC                     15

#define ELF_TYPE_NOTYPE                     0
#define ELF_TYPE_OBJECT                     1
#define ELF_TYPE_FUNC                       2
#define ELF_TYPE_SECTION                    3
#define ELF_TYPE_FILE                       4
#define ELF_TYPE_LOPROC                     13
#define ELF_TYPE_HIPROC                     15

#define MAX_SEARCH_SCOPE                    50

/* 0~15 bits maintain the module temporary flags during loadding, unloadding and searching. */
#define IS_LOADDING(handle) ((handle)->moduleStatus & (1 << 0))
#define IS_UNLOADDING(handle) ((handle)->moduleStatus & (1 << 1))
#define IS_SEARCHING(handle) ((handle)->moduleStatus & (1 << 2))
#define SET_LOADDING(handle) ((handle)->moduleStatus |= (1 << 0))
#define SET_UNLOADDING(handle) ((handle)->moduleStatus |= (1 << 1))
#define SET_SEARCHING(handle) ((handle)->moduleStatus |= (1 << 2))
#define CLEAR_SEARCHING(handle) ((handle)->moduleStatus &= (~(1 << 2)))
#define CLEAR_TMPFLAGS(handle) ((handle)->moduleStatus &= 0xffff0000)
/* 16~31 bits maintain the module status after load. */
#define IS_LOADED(handle) ((handle)->moduleStatus & (1 << 16))
#define IS_TAKEN(handle) ((handle)->moduleStatus & (1 << 17))
#define SET_LOADED(handle) ((handle)->moduleStatus |= (1 << 16))
#define SET_TAKEN(handle) ((handle)->moduleStatus |= (1 << 17))
#define CLEAR_LOADED(handle) ((handle)->moduleStatus &= (~(1 << 16)))
#define CLEAR_TAKEN(handle) ((handle)->moduleStatus &= (~(1 << 17)))
#define CLEAR_MODULESTATUS(handle) ((handle)->moduleStatus = 0)

#define INC_SOHEAD(handle) ((handle)->headCnt++)
#define DEC_SOHEAD(handle) ((handle)->headCnt--)
#define IS_SOHEAD(handle) ((handle)->headCnt > 0)

#define OS_RELOC_COPY_WORD(pDst, pSrc)     \
    do {                                   \
        UINT32 *puwDst = (UINT32 *)(pDst); \
        UINT32 *puwSrc = (UINT32 *)(pSrc); \
        *puwDst = *puwSrc;                 \
        puwDst++;                          \
        puwSrc++;                          \
        *puwDst = *puwSrc;                 \
        puwDst++;                          \
        puwSrc++;                          \
        *puwDst = *puwSrc;                 \
    } while (0)

/* Relocation Type Definitions */
#define OS_R_ARM_NONE                       0 /* No reloc */
#define OS_R_ARM_PC24                       1
#define OS_R_ARM_ABS32                      2
#define OS_R_ARM_REL32                      3
#define OS_R_ARM_LDR_PC_G0                  4
#define OS_R_ARM_ABS16                      5
#define OS_R_ARM_ABS12                      6
#define OS_R_ARM_THM_ABS5                   7
#define OS_R_ARM_ABS8                       8
#define OS_R_ARM_SBREL32                    9
#define OS_R_ARM_THM_CALL                   10
#define OS_R_ARM_THM_PC8                    11
#define OS_R_ARM_BREL_ADJ                   12
#define OS_R_ARM_TLS_DESC                   13
#define OS_R_ARM_THM_SWI8                   14
#define OS_R_ARM_XPC25                      15
#define OS_R_ARM_THM_XPC22                  16
#define OS_R_ARM_COPY                       20 /* Copy symbol at runtime */
#define OS_R_ARM_GLOB_DAT                   21 /* Create GOT entry */
#define OS_R_ARM_JUMP_SLOT                  22 /* Create PLT entry */
#define OS_R_ARM_RELATIVE                   23 /* Adjust by program base */
#define OS_R_ARM_GOTOFF32                   24 /* 32 bit offset to GOT */
#define OS_R_ARM_BASE_PREL                  25 /* 32 bit PC relative offset to GOT */
#define OS_R_ARM_GOT_BREL                   26 /* 32 bit GOT entry */
#define OS_R_ARM_PLT32                      27 /* 32 bit PLT address */
#define OS_R_ARM_CALL                       28
#define OS_R_ARM_THM_JUMP24                 30
#define OS_R_ARM_MOVW_ABS_NC                43
#define OS_R_ARM_MOVT_ABS                   44
#define OS_ARM_THM_MOVW_ABS_NC              47
#define OS_ARM_THM_MOVT_ABS                 48

/* These are GNU extensions to enable C++ vtable garbage collection. */
#define OS_R_ARM_GNU_VTENTRY                100
#define OS_R_ARM_GNU_VTINHERIT              101

#define OS_R_ARM_THM_JUMP11                 102 /* Thumb unconditional branch */
#define OS_R_ARM_THM_JUMP8                  103 /* Thumb conditional branch */
#define OS_R_ARM_RXPC25                     249
#define OS_R_ARM_RSBREL32                   250
#define OS_R_ARM_THM_RPC22                  251
#define OS_R_ARM_RREL32                     252
#define OS_R_ARM_RABS32                     253
#define OS_R_ARM_RPC24                      254
#define OS_R_ARM_RBASE                      255

#define OS_R_ARM_AARCH64_ABS64              257 /* Direct 64 bit. */
#define OS_R_ARM_AARCH64_ADR_PREL_PG_HI21   275
#define OS_R_ARM_AARCH64_ADD_ABS_LO12_NC    277
#define OS_R_ARM_AARCH64_LDST8_ABS_LO12_NC  278
#define OS_R_ARM_AARCH64_CALL26             283
#define OS_R_ARM_AARCH64_LDST16_ABS_LO12_NC 284
#define OS_R_ARM_AARCH64_LDST32_ABS_LO12_NC 285
#define OS_R_ARM_AARCH64_LDST64_ABS_LO12_NC 286

/* AARCH64 for  Dynamic relocations */
#define OS_R_ARM_AARCH64_COPY               1024
#define OS_R_ARM_AARCH64_GLOB_DAT           1025
#define OS_R_ARM_AARCH64_JUMP_SLOT          1026
#define OS_R_ARM_AARCH64_RELATIVE           1027
#define OS_R_ARM_AARCH64_TLS_DTPREL64       1028
#define OS_R_ARM_AARCH64_TLS_DTPMOD64       1029
#define OS_R_ARM_AARCH64_TLS_TPREL64        1030
#define OS_R_ARM_AARCH64_TLSDESC            1031
#define OS_R_ARM_AARCH64_IRELATIVE          1032

#define OS_BIT_AND(x, y) ((x) & (y))

/**
* Macro capable of relocating ARM symbols
*/

/* Macro capable of modifying the lower X bits */
#define OS_INSERT(buf, value, mask) ((buf) = (((buf) & ~(mask)) | ((value) & (mask))))

#define OS_LOW24_INSERT(buf, value) OS_INSERT((buf), (value), 0x00ffffff)

#define OS_LOW16_INSERT(buf, value) OS_INSERT((buf), (value), 0x0000ffff)

#define OS_LOW12_INSERT(buf, value) OS_INSERT((buf), (value), 0x00000fff)

#define OS_LOW11_INSERT(buf, value) OS_INSERT((buf), (value), 0x000007ff)

#define OS_LOW8_INSERT(buf, value) OS_INSERT((buf), (value), 0x000000ff)

#define OS_BITS6_10_INSERT(buf, value) OS_INSERT((buf), (value) << 6, 0x000007c0)

#define OS_UBIT_INSERT(buf, value) OS_INSERT((buf), (value) << 23, 0x0080000)

/* The values of n bits of a binary number are 1 */
#define OS_MASK(n) (((AARCHPTR)1 << (n)) - 1)

/* The value of bit n of a binary number is 1 */
#define OS_SIGN_BIT_MASK(n) ((AARCHPTR)1 << ((n)-1))

/* Add an extended bit to the upper bit */
#define OS_SIGN_EXTEND(val, nBits) \
    (((OS_MASK(nBits) & ((AARCHPTR)val)) ^ OS_SIGN_BIT_MASK(nBits)) - OS_SIGN_BIT_MASK(nBits))

#define OS_SIGN_SHRINK(val, nBits) (OS_MASK(nBits) & (val))

/* Check whether the extended bit value falls within the nBits range */
#define OS_CHECK_FITS(val, bits)                                      \
    (((((AARCHPTR)val) & ~(((AARCHPTR)1 << ((bits)-1)) - 1)) == 0) ||  \
        ((((AARCHPTR)val) & ~(((AARCHPTR)1 << ((bits)-1)) - 1)) == ~(AARCHPTR)(((AARCHPTR)1 << ((bits)-1)) - 1)))

#define OS_MEM_READ_32(relocAdrs, offset) ((offset) = *((UINT32 *)(relocAdrs)))
#define OS_MEM_READ_16(relocAdrs, offset) ((offset) = *((UINT16 *)(relocAdrs)))
#define OS_MEM_READ_8(relocAdrs, offset) ((offset) = *((UINT8 *)(relocAdrs)))

#define OS_MEM_WRITE_32(relocAdrs, value32) \
    (*((UINT32 *)(relocAdrs)) = (value32))
#define OS_MEM_WRITE_16(relocAdrs, value16) \
    (*((UINT16 *)(relocAdrs)) = (value16))
#define OS_MEM_WRITE_8(relocAdrs, value8) \
    (*((UINT8 *)(relocAdrs)) = (value8))

#define LD_ELF_RELOC_CopyByWord(dst, src) \
    do {                                    \
        UINT32 *dstTmp = (UINT32 *)(dst);  \
        UINT32 *srcTmp = (UINT32 *)(src);  \
        *dstTmp = *srcTmp;                  \
        dstTmp++;                           \
        srcTmp++;                           \
        *dstTmp = *srcTmp;                  \
        dstTmp++;                           \
        srcTmp++;                           \
        *dstTmp = *srcTmp;                  \
    } while (0)

#define OS_READ_INSTRUCTION_THUMB16(position) \
    (*(UINT16 *)(position))
#define OS_WRITE_INSTRUCTION_THUMB16(position, value16) \
    (*(UINT16 *)(position) = (UINT16)(value16))

#define OS_READ_INSTRUCTION_THUMB32(position) \
    (*(UINT16 *)(position) + ((UINT32)(*(UINT16 *)((UINT32)(position) + 2)) << 16))
#define OS_WRITE_INSTRUCTION_THUMB32(position, value32)                          \
    do {                                                                         \
        *(UINT16 *)(position) = (UINT16)(value32);                               \
        *(UINT16 *)((UINT32)(position) + 2) = (UINT16)((UINT32)(value32) >> 16); \
    } while (0)

#define GET_BITS(bitWide) ((AARCHPTR)(-1) >> ((sizeof(AARCHPTR) * 8) - (bitWide)))

#define CLEAR_BITS_AREA(data, ucStartBit, bitWide) \
    (data) &= ~(GET_BITS(bitWide) << ucStartBit);

#define GET_BITS_AREA(data, startBit, bitWide) (data & (GET_BITS(bitWide) << startBit))

#define OS_GET_PAGE_ADDR(expr) ((expr) & ~(AARCHPTR)0xfff)

typedef struct {
    UINT32 relocType;
    UINT32 shType;  // REL section or RELA section.
    AARCHPTR position;  // the address of the place being relocated
    AARCHPTR symAdd;  // the address of the symbol
    ssize_t addend;  // the addend for the relocation
    AARCHPTR baseSegment;  // the addressing origin of the output segment defining the symbol S.
    AARCHPTR gotAddr;  // the address of the Global Offset Table
    UINT8 thumbFunc;  // T is 1 if the target symbol S has type STT_FUNC and the symbol addresses a Thumb instruction; it is 0 otherwise.
    UINT8 reserved[7];
} LDRelocParam;

typedef struct { /* LDSymbol - entry in symbol table */
    CHAR *name; /* Pointer to symbol name */
    INT8 *addr; /* Symbol address */
} LDSymbol;

/* Data structures */
typedef struct {
    UINT32 *loadScnHdrIdxs; /* Loadable sections header index tbl */
    UINT32 *symTabScnHdrIdxs; /* Sym table sections header idx tbl */
    UINT32 *relScnHdrIdxs; /* Reloc info sections header idx tbl */
    UINT32 *strTabScnHdrIdxs; /* Str table sections header idx tbl */
} LDIdxTbls;

typedef struct {
    VOID *addr; /* Symbol's address */
    INT8 type; /* Symbol's type */
} LDSymInfo;

typedef struct {
    LOS_DL_LIST linkNode;
    AARCHPTR symAddr;
} DynComSymInfo;

enum ElfType {
    ET_NONE = LD_ET_NONE,
    ET_OBJ = LD_ET_REL,
    ET_EXEC = LD_ET_EXEC,
    ET_SO = LD_ET_DYN
};

typedef struct {
    UINT32 strLen;
    UINT32 symNum;
    UINT32 strTotalLen;
    UINT32 symTotalNum;
    LDSymbol *symTbl;
    CHAR *strTbl;
} SymbolManage;

typedef struct DynModuleInfo {
    LOS_DL_LIST manageList; /* Must comes first */
    LOS_DL_LIST comSymInfoList;
    AARCHPTR segmentsAddr;
    AARCHPTR segmentsSize;
    UINT32 moduleStatus;
    INT32 refCnt;
    INT32 headCnt;
    enum ElfType type;
    CHAR *moduleFileName;
    SymbolManage symManage;
    struct DynModuleInfo *apDlNeeded[N_MAX_MODULES];
} DynModuleInfo;

enum {
    EN_REL,
    EN_RELA,
    EN_JMPREL
};

typedef struct {
    AARCHPTR relTable;  // address of relocation table.
    UINT32 relTableSize;  // total size of relocation table.
    UINT32 relEntrySize;  // size of every relocation entry.
} RelocationInfo;

typedef struct {
    RelocationInfo rel;
    RelocationInfo rela;
    RelocationInfo jmpRel;
} RelocationInfoT;

#define RELOCATIONINFO_COUNT               (sizeof(RelocationInfoT) / sizeof(RelocationInfo))

typedef struct {
    LOS_DL_LIST dlLinkNode;

    AARCHPTR loadStart;
    AARCHPTR loadBias;

    AARCHPTR minVaddr;  // the minimum virtual address of load-segment.

    DynModuleInfo *moduleHandle;

    LD_ELF_EHDR *elfHdr;

    LD_ELF_SHDR *scnHdrTbl;
    UINT32 scnNum;

    LD_ELF_PHDR *phdrTbl;
    UINT32 phNum;

    LD_ELF_DYN *dynamic;

    CHAR *strTab;
    AARCHPTR strTabSize;

    LD_ELF_SYM *symTab;
    UINT32 symNum;

    union {
        RelocationInfoT relocations;
        RelocationInfo rellocationInfos[RELOCATIONINFO_COUNT];
    } relocation;
} DlInfo;

/* initLib */
extern DynModuleInfo g_sysModuleInfo[N_MAX_MODULES];

typedef INT32 (*LOAD_FUNC)(DlInfo *dlInfo);
typedef INT32 (*RELOC_FUNC)(DlInfo *dlInfo);

/* commLib */
extern BOOL LD_ElfSymIsVisible(LD_ELF_SYM *symbol);
extern INT8 LD_ElfSymTypeGet(LD_ELF_SYM *symbol, LD_ELF_SHDR *scnHdrTbl);

/* symtblLib */
extern UINT32 LD_CommonManage(DynModuleInfo *moduleHandle, AARCHPTR comAreaSize, AARCHPTR comAlignment, CHAR *symName,
                              VOID **symAddr, INT8 *symType);

/* moduleLib */
extern DynModuleInfo *LD_ModuleHandleAlloc(CHAR *elfFileName);
extern INT32 LD_ModuleMemRelease(DynModuleInfo *moduleHandle);
extern INT32 LD_ModuleDeinit(DynModuleInfo *moduleHandle);
extern INT32 LD_ModuleUnload(VOID *handle);
extern INT32 LD_DlLoadAll(DynModuleInfo *moduleHandle, LOAD_FUNC loadFunc);
extern INT32 LD_DlRelocAll(RELOC_FUNC relocFunc);
extern INT32 LD_ObjLoad(DlInfo *dlInfo);

/* symLib */
extern UINT32 LD_SymAdd(DynModuleInfo *moduleHandle, CHAR *name, INT8 *addr, INT8 type);
extern INT32 LD_SymRemoveByModuleHandle(DynModuleInfo *moduleHandle);

extern UINT32 LD_ObjARMReloc(DlInfo *dlInfo, LD_ELF_SHDR *relHdr);
extern INT32 LD_ObjReloc(DlInfo *dlInfo);

extern INT32 LD_DlSegReloc(DlInfo *dlInfo);
extern VOID *LD_FindSymInDepended(VOID *handle, CHAR *name);

extern UINT32 LD_SymLoadAddrGet(DlInfo *dlInfo, UINT32 symIndex, AARCHPTR *loadAddr);
extern VOID LD_FlushICache(VOID);
extern VOID LD_FlushDCache(AARCHPTR start, AARCHPTR end);

#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */

#endif /* _KERNEL_LOS_LD_TYPE_INC */
